#include "Sock.hpp"
#include <iostream>
#include <sys/epoll.h>
#include <string>

#define SIZE 128
#define NUM 64

void usage(string s)
{
    std::cout << s << " " << "port" << std::endl;
}

int main(int args, char *argv[])
{
    if (args != 2)
    {
        usage(argv[0]);
        exit(1);
    }

    // 1.创建listen套接字
    int listenfd = Socket::Sock();

    // 2.绑定套接字
    int16_t port = (uint16_t)atoi(argv[1]);
    Socket::Bind(listenfd, port);

    // 3.监听套接字
    Socket::Listen(listenfd);

    // 4.监听完套接字之后，不能立即accept
    // accept本质上也是从listenfd这个文件描述符中获取连接
    // 所以必须得等listenfd这个文件描述符就绪之后，accept再去获取连接
    // 那么listenfd什么时候就绪呢，三次握手建立连接之后就就绪了
    // 所以此时得先调用epoll_create 接口，创建epollfd
    int epfd = epoll_create(SIZE);
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listenfd;
    // 5.将listenfd添加进epfd，让epfd存储listenfd的读事件
    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
    struct epoll_event fd_arry[NUM];
    // 循环等待
    for (;;)
    {
        for(int i;i<NUM;i++)
        {
            fd_arry[i].events=0;
            fd_arry[i].data={0};

        }
        //6.进程调用epoll_wait接口进行等待，同时操作系统开始监测对应的文件描述符对应的事件是否就绪
        int n = epoll_wait(epfd,fd_arry,NUM,-1);

        switch (n)
        {
        case -1:
            std::cout << "poll error" << std::endl;
            break;
        case 0:
            std::cout << "等待超时" << std::endl;
            break;
        default:
            // 意味着epoll等待成功
            for (int i = 0; i < n; i++)
            {
                // 判断fd_arry中要等待的文件描述符的读事件是否就绪
                if (fd_arry[i].events & EPOLLIN)
                {
                    int sock=fd_arry[i].data.fd;
                    // struct poolfd 中的读事件就绪
                    std::cout << "sock: " << sock << " 上面有了读事件，可以读取了" << std::endl;
                    // fd_arry[i].fd读就绪
                    // 但是要判断这个fd_farry[i].fd是listenfd就绪还是普通fd就绪
                    if (sock== listenfd)
                    {
                        std::cout << "listen_sock: " << listenfd << " 有了新的链接到来" << std::endl;
                        // listenfd就绪了，就可以开始获取连接了
                       // int newfd = Socket::Accept(listenfd);
                        // 将连接读走之后，此时一定要设置listenfd的读不就绪，方便获取下一个连接
                        // fd_arry[i].revents=0;
                       /*  if (newfd >= 0)
                        {
                            std::cout << "listen_sock: " << listenfd << " 获取新的链接成功" << std::endl;
                            
                        // 获取了新连接的文件描述符，但是后续read要进行读取时，还得等待newfd就绪
                        // 所以就让epoll去等待newfd就绪

                        // 将newfd及等待的事件添加进epfd中
                        struct epoll_event ev;
                        ev.events=EPOLLIN;
                        ev.data.fd=newfd;
                        epoll_ctl(epfd,EPOLL_CTL_ADD, newfd, &ev); 
                        std::cout<< newfd <<"以及需要等待的"<<newfd<<"的事件已经被添加到了epfd"<<epfd<<std::endl<<std::endl;
                        } */

                    }
                    else
                    {
                        // fd_arry[i].fd是普通文件描述符就绪了，所以可以开始进行数据拷贝，即读取数据
                        std::cout<<"普通文件描述符"<<sock<<"的读事件已经就绪，可以进行读取了"<<std::endl;
                        char buffer[1024];
                        int s = read(sock, buffer, sizeof(buffer) - 1);
                        // fd_arry[i].revents=0;
                        if (s > 0)
                        {
                            buffer[s] = 0;
                            std::cout << "client: " << buffer << std::endl;
                        }
                        else if (s == 0)
                        {
                            // 对端关闭了连接
                            // 此时就要将当前fd_arry[i].fd从fd_arry数组中移除，因为对端关闭了连接
                            // 意味着再也不会向fd_arry[i].fd这个文件描述符的接收缓冲区中发送数据
                            // 也就意味着fd_arry[i].fd这个文件描述符的接收缓冲区永远为空
                            // 即再也不会就绪，所以等待fd_arry[i].fd就绪就是毫无意义的，所以不必再等待
                            std::cout<<"client 关闭了连接,sever 也要关闭连接"<<std::endl;
                            close(sock);
                            epoll_ctl(epfd,EPOLL_CTL_DEL,sock,&fd_arry[i]);
                            
                        }
                        else
                        {
                            // 读取出错
                            std::cout << " read error!" << std::endl;
                            close(sock);
                            epoll_ctl(epfd,EPOLL_CTL_DEL,sock,&fd_arry[i]);
                        }
                    }
                }
            }
            break;
        }
    }
    close(epfd);
    close(listenfd);
    return 0;
}
